CM3D2 Converter.menu_file

  1import bpy
  2import math
  3import mathutils
  4import struct
  5from . import common
  6from . import compat
  7from .translations.pgettext_functions import *
  8
  9
 10
 11PROP_OPTS = {'LIBRARY_EDITABLE'}
 12
 13# New specially handled commands need to have 3 things
 14#   1) An enum entry in COMMAND_ENUMS (if it is not already in there)
 15#   2) A class decorated with @CM3D2MenuCommand([enum1, [enum2, [...]]], name="{command_name}" )
 16#   3) A collection property to hold that class in OBJECT_PG_CM3D2Menu
 17# The decorators will handle the rest
 18
 19COMMAND_ENUMS = [
 20    ('', "Menu Meta", ''),
 21    ('end'          , "End"                    , "description", 'X'                    ,   0),
 22    ('name'         , "Menu Name"              , "description", 'FILE_TEXT'            ,   3),
 23    ('saveitem'     , "Menu Category"          , "description", 'FILE_TEXT'            ,   4),
 24    ('setumei'      , "Menu Description"       , "description", 'FILE_TEXT'            ,   5),
 25    ('priority'     , "Priority"               , "description", 'SORT_DESC'            ,   6),
 26    ('メニューフォルダ'     , "Folder"                 , "description", 'FILE_FOLDER'          ,   7),
 27    ('icon'         , "Icon"                   , "description", 'FILE_IMAGE'           ,  10),
 28    ('icons'        , "Icon (Small)"           , "description", 'FILE_IMAGE'           ,  11), # alias of 'icon'
 29    ('iconl'        , "Icon (Large)"           , "Unused"     , 'BLANK1'               ,  12),
 30    
 31    ('', "Item Meta", ''),
 32    ('ver'          , "Item Version"           , "description", 'FILE_TEXT'            ,  20),
 33    ('category'     , "Item Category"          , "description", 'SORTALPHA'            ,  21),
 34    ('catno'        , "Item Category Number"   , "description", 'LINENUMBERS_ON'       ,  22),
 35    ('アイテム'         , "Item"                   , "description", 'FILE_3D'              ,  30),
 36    ('アイテム条件'       , "Item Conditions"        , "description", 'SCRIPTPLUGINS'        ,  31),
 37    ('if'           , "Item If"                , "description", 'FILE_SCRIPT'          ,  32),
 38    ('アイテムパラメータ'    , "Item Parameters"        , "description", 'SCRIPTPLUGINS'        ,  33),
 39    ('半脱ぎ'          , "Item Half Off"          , "description", 'LIBRARY_DATA_INDIRECT',  34),
 40    ('リソース参照'       , "Item Resource Reference", "description", 'LIBRARY_DATA_INDIRECT',  35),  # alias of '半脱ぎ'
 41    
 42    ('', "Item Control", ''),
 43    ('set'          , "Set"                    , "Unused"     , 'BLANK1'               ,  40),
 44    ('setname'      , "Set Name"               , "Unused"     , 'BLANK1'               ,  41),
 45    ('setslotitem'  , "Set Slot Item"          , "description", 'FILE_TICK'            ,  42),
 46    ('additem'      , "Add Item"               , "description", 'ADD'                  ,  43),
 47    ('unsetitem'    , "Unset Item"             , "description", 'REMOVE'               ,  44),
 48    ('nofloory'     , "Disable Item Floor"     , "description", 'CON_FLOOR'            ,  45),
 49    ('maskitem'     , "Mask Item"              , "description", 'MOD_MASK'             ,  46),
 50    ('delitem'      , "Delete Item"            , "description", 'TRASH'                ,  47),
 51    ('node消去'       , "Node Hide"              , "description", 'HIDE_ON'              ,  50),
 52    ('node表示'       , "Node Display"           , "description", 'HIDE_OFF'             ,  51),
 53    ('パーツnode消去'    , "Parts-Node Hide"        , "description", 'VIS_SEL_01'           ,  52),
 54    ('パーツnode表示'    , "Parts-Node Display"     , "description", 'VIS_SEL_11'           ,  53),
 55
 56    ('', "Material Control", ''),
 57    ('color'        , "Color"                  , "description", 'COLOR'                ,  60),
 58    ('mancolor'     , "Man Color"              , "description", 'GHOST_ENABLED'        ,  61),
 59    ('color_set'    , "Color-Set"              , "description", 'GROUP_VCOL'           ,  62),
 60    ('tex'          , "Texture"                , "description", 'TEXTURE'              ,  70),
 61    ('テクスチャ変更'      , "Texture Change"         , "description", 'TEXTURE'              ,  71), # alias of 'tex'
 62    ('テクスチャ乗算'      , "Texture Multiplication" , "description", 'FORCE_TEXTURE'        ,  72),
 63    ('テクスチャ合成'      , "Texture Composition"    , "description", 'NODE_TEXTURE'         ,  73),
 64    ('テクスチャセット合成'   , "Texture Set Composition", "description", 'NODE_TEXTURE'         ,  74),
 65    ('マテリアル変更'      , "Material Change"        , "description", 'MATERIAL'             ,  80),
 66    ('useredit'     , "Material Properties"    , "description", 'MATERIAL'             ,  81),
 67    ('shader'       , "Shader"                 , "description", 'SHADING_RENDERED'     ,  90),
 68
 69    ('', "Maid Control", ''),
 70    ('prop'         , "Property"               , "description", 'PROPERTIES'           , 100),
 71    ('アタッチポイントの設定'  , "Attach Point"           , "description", 'HOOK'                 , 110),
 72    ('blendset'     , "Face Blend-Set"         , "description", 'SHAPEKEY_DATA'        , 120),
 73    ('paramset'     , "Face Parameter-Set"     , "description", 'OPTIONS'              , 121),
 74    ('commenttype'  , "Profile Comment Type"   , "description", 'TEXT'                 , 130),
 75    ('bonemorph'    , "Bone Morph"             , "description", 'CONSTRAINT_BONE'      , 140),
 76    ('length'       , "Hair Length"            , "description", 'CONSTRAINT_BONE'      , 141),
 77    ('anime'        , "Animation"              , "description", 'ANIM'                 , 150),
 78    ('animematerial', "Animation (Material)"   , "description", 'ANIM'                 , 151),
 79    ('param2'       , "Parameter 2"            , "description", 'CON_TRANSFORM'        , 160),
 80
 81    ('', "Misc.", ''),
 82    ('setstr'       , "Set String"             , "Unused"     , 'BLANK1'               , 170),
 83    ('onclickmenu'  , "onclickmenu"            , "Decorative" , 'NONE'                 , 200),
 84    ('属性追加'         , "addattribute"           , "Decorative" , 'NONE'                 , 201)                 
 85]
 86
 87COMMAND_TYPE_LIST = dict() # filled by the CM3D2MenuCommand decorator
 88
 89def get_command_enum_info(enum_string, enum_items=COMMAND_ENUMS):
 90    if enum_string == '':
 91        return None
 92    for enum_info in enum_items:
 93        if enum_info[0] == enum_string:
 94            return enum_info
 95    return None
 96
 97def get_command_enum_name(enum_string, enum_items=COMMAND_ENUMS):
 98    if enum_string == '':
 99        return ''
100    for enum_info in enum_items:
101        if enum_info[0] == enum_string:
102            return enum_info[1]
103    return enum_string
104
105
106
107
108''' CM3D2 Menu Sub-Classes '''
109
110@compat.BlRegister()
111class CM3D2MENU_PG_CommandPointer(bpy.types.PropertyGroup):
112    bl_idname = 'CM3D2MenuCommandPointer'
113
114    collection_name  = bpy.props.StringProperty(options={'HIDDEN'})
115    prop_index       = bpy.props.IntProperty   (options={'HIDDEN'})
116
117    def dereference(self, data):
118        return getattr(data, self.collection_name)[self.prop_index]
119
120
121@compat.BlRegister()
122class MISCCOMMAND_PG_Param(bpy.types.PropertyGroup):
123    bl_idname = 'CM3D2MenuParam'
124
125    # Really the value should be saved, not the name, but template_list() doesn't like that so they're switched.
126    def _s(self, value):
127        self.name = value
128
129    #name = bpy.props.StringProperty(name="Name", options=PROP_OPTS, get=lambda self : self.value)
130    name   = bpy.props.StringProperty(name="Name", default="param", options={'HIDDEN'})
131    value  = bpy.props.StringProperty(name="Slot Name", options={'SKIP_SAVE'}, default="param", set=_s, get=lambda self: self.name)
132
133
134
135
136''' Decorator for CM3D2 Menu Command Classes '''
137class CM3D2MenuCommand():
138    def __init__(self, *args, name="{command_name}"):
139        self.name_template = name
140        self.command_enums = set()
141        for command in args:
142            for enum_info in COMMAND_ENUMS:
143                if enum_info[0] == command:
144                    self.command_enums.add(enum_info)
145                    break
146            continue
147    
148    def __call__(self, cls):
149        cls.name_template = self.name_template
150        cls.command_enums = self.command_enums
151        # Associate the class with its command in COMMAND_CLASSES
152        for enum in cls.command_enums:
153            COMMAND_TYPE_LIST[enum[0]] = cls
154
155        # define command property
156        if len(cls.command_enums) > 0:
157            cls.command = bpy.props.EnumProperty(
158                items   = cls.command_enums,
159                name    = "Command",
160                options = PROP_OPTS,
161                default = next(iter(cls.command_enums))[0],
162                description = "The command of this menu file command-chunk"
163            )
164        else:
165            cls.command = bpy.props.StringProperty(
166                name    = "Command",
167                options = PROP_OPTS,
168                default = "command",
169                description = "The command of this menu file command-chunk"
170            )
171
172        cls.index = bpy.props.IntProperty(name="Index", options=PROP_OPTS)
173        #cls.initalized = bpy.props.BoolProperty(name="Index", options={'SKIP_SAVE'}, default=False)
174
175        attributes = dir(cls)
176        cls.name_format_attributes = set()
177        for attr in attributes:
178            if "{"+attr+"}" in cls.name_template:
179                cls.name_format_attributes.add(attr)
180
181        def format_name(self):
182            params = { attr : getattr(self, attr) for attr in cls.name_format_attributes }
183            if len(cls.command_enums) > 0:
184                params['command_name'] = get_command_enum_name(self.command, enum_items=cls.command_enums)
185            else:
186                params['command_name'] = get_command_enum_name(self.command)
187            return cls.name_template.format(**params)
188        
189        cls.format_name = format_name
190        
191        # define name property
192        cls.name = bpy.props.StringProperty(
193            name    = "Name"                 ,
194            options = {'HIDDEN', 'SKIP_SAVE'},
195            get     = cls.format_name
196        )
197
198        # add catch and rethrow for functions
199        def catch_throw_wrap(func, gerund, catch_type, throw_type=None):
200            throw_type = throw_type or catch_type
201            prefix = f_tip_("Error {gerund} {bl_idname}: ", gerund=gerund, bl_idname=cls.bl_idname) + "{message}"
202            def _f(*args, **kwargs):
203                try:
204                    return func(*args, **kwargs)
205                except catch_type as e:
206                    raise throw_type(prefix.format(message=e.args[0]))
207            return _f
208
209        cls.parse_list = catch_throw_wrap(cls.parse_list, "parsing", ValueError)
210        cls.pack_into  = catch_throw_wrap(cls.pack_into , "packing", ValueError)
211
212        return cls
213
214
215
216
217''' CM3D2 Menu Command Classes '''
218
219@compat.BlRegister()
220@CM3D2MenuCommand('アタッチポイントの設定', name="{command_name} : {point_name}")
221class CM3D2MENU_PG_AttachPointCommand(bpy.types.PropertyGroup):
222    bl_idname = 'CM3D2MenuAttachPointCommand'
223    '''
224    アタッチポイントの設定
225      ├ point_name(呼び出し名)
226      ├ location.x(座標)
227      ├ location.y(座標)
228      ├ location.z(座標)
229      ├ rotation.x(軸回転角度)[範囲:0±180°]
230      ├ rotation.y(軸回転角度)[範囲:0±180°]
231      └ rotation.z(軸回転角度)[範囲:0±180°]
232    '''
233    point_name = bpy.props.StringProperty     (name="Point Name", default="Attach Point", description="Name of the slot to define the attatchment point for" , options=PROP_OPTS)
234    location   = bpy.props.FloatVectorProperty(name="Location"  , default=(0, 0, 0)     , description="Location of the attatchment relative to the base bone", options=PROP_OPTS, subtype=compat.subtype('TRANSLATION'))
235    rotation   = bpy.props.FloatVectorProperty(name="Rotation"  , default=(0, 0, 0)     , description="Rotation of the attatchment relative to the base bone", options=PROP_OPTS, subtype=compat.subtype('EULER'      ))
236
237    def parse_list(self, string_list):
238        self.command = string_list[0]
239        self.point_name  = string_list[1]
240        self.location.x = float(string_list[2])
241        self.location.y = float(string_list[3])
242        self.location.z = float(string_list[4])
243        self.rotation.x = float(string_list[5]) * math.pi/180
244        self.rotation.y = float(string_list[6]) * math.pi/180
245        self.rotation.z = float(string_list[7]) * math.pi/180
246    
247    def pack_into(self, buffer):
248        buffer = buffer + struct.pack('<B', 1 + 1 + 3 + 3)
249        buffer = common.pack_str(buffer, self.command   )
250        buffer = common.pack_str(buffer, self.point_name)
251        buffer = common.pack_str(buffer, str(self.location.x)              )
252        buffer = common.pack_str(buffer, str(self.location.y)              )
253        buffer = common.pack_str(buffer, str(self.location.z)              )
254        buffer = common.pack_str(buffer, str(self.rotation.x * 180/math.pi))
255        buffer = common.pack_str(buffer, str(self.rotation.y * 180/math.pi))
256        buffer = common.pack_str(buffer, str(self.rotation.z * 180/math.pi))
257
258        return buffer
259
260    def draw(self, context, layout):
261        layout.label(text=self.name)
262
263        col = layout.column()
264        col.alignment = 'RIGHT'
265        col.prop(self, 'command', translate=False)
266        col.label(text=self.command + "     ", translate=False)
267
268        col = layout.column()
269        col.prop(self, 'point_name')
270        col.prop(self, 'location'  )
271        col.prop(self, 'rotation'  )
272        
273        col = layout.column(align=True)
274        col.operator('cm3d2menu.align_selected_to_attach_point', icon=compat.icon('OBJECT_ORIGIN')    )
275        col.operator('cm3d2menu.align_attach_point_to_selected', icon=compat.icon('ORIENTATION_LOCAL'))
276
277
278@compat.BlRegister()
279@CM3D2MenuCommand('prop', name="{command_name} : {prop_name} = {value}")
280class CM3D2MENU_PG_PropertyCommand(bpy.types.PropertyGroup):
281    bl_idname = 'CM3D2PropertyMenuCommand'
282    '''
283    prop
284      ├ prop_name
285      └ value
286    '''
287    prop_name = bpy.props.StringProperty(name="Property Name" , default="prop name", description="Name of the property to set on load" , options=PROP_OPTS)
288    value     = bpy.props.FloatProperty (name="Property Value", default=50         , description="Value of the property to set on load", options=PROP_OPTS)
289    
290    def parse_list(self, string_list):
291        self.command    = string_list[0]
292        self.prop_name  = string_list[1]
293        self.value      = float(string_list[2])
294    
295    def pack_into(self, buffer):
296        buffer = buffer + struct.pack('<B', 1 + 1 + 1)
297        buffer = common.pack_str(buffer, self.command    )
298        buffer = common.pack_str(buffer, self.prop_name  )
299        buffer = common.pack_str(buffer, str(self.value) )
300
301        return buffer
302
303    def draw(self, context, layout):
304        col = layout.column()
305        col.alignment = 'RIGHT'
306        col.prop(self, 'command', translate=False)
307        col.label(text=self.command + "     ", translate=False)
308
309        col = layout.column()
310        col.label(text=self.command, translate=False)
311        col.prop(self, 'prop_name')
312        col.prop(self, 'value'    )
313
314
315@compat.BlRegister()
316@CM3D2MenuCommand(name="{command_name}")
317class CM3D2MENU_PG_MiscCommand(bpy.types.PropertyGroup):
318    bl_idname = 'CM3D2MenuMiscCommand'
319    '''
320    command
321      ├ child_0
322      ├ child_1
323      ├ child_2
324      ├ ...
325      ├ child_n-1
326      └ child_n
327    '''
328    params = bpy.props.CollectionProperty(name="Parameters", options=PROP_OPTS, type=MISCCOMMAND_PG_Param)
329    
330    active_index = bpy.props.IntProperty(options={'HIDDEN'})
331
332    search = bpy.props.BoolProperty(name="Search", default=False, description="Search for suggestions", options=PROP_OPTS)
333
334    def new_param(self):
335        new_param = self.params.add()
336        new_param.value = "newparam"
337        return new_param
338
339    def remove_param(self, index: int):
340        return self.params.remove(index)
341
342    def move_param(self, old_index, new_index):
343        return self.params.move(old_index, new_index)
344
345    def parse_list(self, string_list):
346        self.command = string_list[0]
347        for param in string_list[1:]:
348            new_param = self.params.add()
349            new_param.value = param
350            new_param.name  = param
351
352    def pack_into(self, buffer):
353        buffer = buffer + struct.pack('<B', 1 + len(self.params))
354        buffer = common.pack_str(buffer, self.command)
355        for param in self.params:
356            buffer = common.pack_str(buffer, param.value)
357        return buffer
358    
359    def draw(self, context, layout):
360        enum_info = get_command_enum_info(self.command)
361        if enum_info:
362            layout.label(text=enum_info[1], icon=enum_info[3])
363
364        row = layout.row(align=True)
365        if not compat.IS_LEGACY:
366            row.use_property_split = False
367        if self.search:
368            search_data = bpy.ops.cm3d2menu.command_add.get_rna_type().properties.get('type')
369            row.prop_search(self, 'command', search_data, 'enum_items', text="", translate=True, icon='VIEWZOOM')
370        else:
371            row.prop(self, 'command', text="")
372        row.prop(self, 'search', text='', icon='ZOOM_OUT' if self.search else 'VIEWZOOM')
373        
374        
375        row = layout.row()
376        row.template_list('UI_UL_list', 'CM3D2MENU_UL_misc_command_children',
377            self, 'params'      ,
378            self, 'active_index',
379            rows    = 3,
380            maxrows = 8,
381        )
382        sub_col = row.column(align=True)
383        sub_col.operator('cm3d2menu.param_add'   , icon='ADD'   , text="")
384        sub_col.operator('cm3d2menu.param_remove', icon='REMOVE', text="")
385        #sub_col.separator()
386        #sub_col.menu("OBJECT_MT_cm3d2_menu_context_menu", icon='DOWNARROW_HLT', text="")
387        if self.active_index < len(self.params):
388            sub_col.separator()
389            sub_col.operator("cm3d2menu.param_move", icon='TRIA_UP'  , text="").direction = 'UP'  
390            sub_col.operator("cm3d2menu.param_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
391
392
393
394
395''' CM3D2 Menu Class '''
396
397# This generates OBJECT_PG_CM3D2Menu.command_type_collections
398def generate_command_type_collections(cls):
399    prop_example = bpy.props.CollectionProperty(type=bpy.types.PropertyGroup)
400    cls.command_type_collections = {}
401
402    for prop_name in dir(cls):
403        if prop_name == "commands":
404            continue
405
406        prop = getattr(cls, prop_name)
407        command_class = None
408        if type(prop) != type(prop_example):
409            continue
410        if "len" in dir(prop):
411            if len(prop)  != len(prop_example) :
412                continue
413            if prop[0]    != prop_example[0]   :
414                continue
415            command_class = prop[1]["type"]
416
417        # Fix for Blender 2.93
418        elif "function" in dir(prop) and "keywords" in dir(prop) and "type" in prop.keywords.keys(): 
419            if prop.function != prop_example.function:
420                continue
421            command_class = prop.keywords["type"]
422        else:
423            continue
424
425        if command_class == CM3D2MENU_PG_CommandPointer:
426            continue
427
428        cls.command_type_collections[command_class.bl_idname] = prop_name
429        #print(cls.bl_idname+".command_type_collections[\""+prop_name+"\"]", "=", prop)
430    
431    return cls
432
433@compat.BlRegister()
434@generate_command_type_collections
435class OBJECT_PG_CM3D2Menu(bpy.types.PropertyGroup):
436    bl_idname = 'CM3D2Menu'
437
438    version     = bpy.props.IntProperty   (name="Version"    , options=PROP_OPTS, min=0, step=100    )
439    path        = bpy.props.StringProperty(name="Path"       , options=PROP_OPTS, subtype='FILE_PATH')
440    name        = bpy.props.StringProperty(name="Name"       , options=PROP_OPTS)
441    category    = bpy.props.StringProperty(name="Category"   , options=PROP_OPTS)
442    description = bpy.props.StringProperty(name="Description", options=PROP_OPTS)
443                                                                       
444    attach_point_commands = bpy.props.CollectionProperty(type=CM3D2MENU_PG_AttachPointCommand, options={'HIDDEN'})
445    property_commands     = bpy.props.CollectionProperty(type=CM3D2MENU_PG_PropertyCommand   , options={'HIDDEN'})
446    misc_commands         = bpy.props.CollectionProperty(type=CM3D2MENU_PG_MiscCommand       , options={'HIDDEN'})
447
448    commands = bpy.props.CollectionProperty(name="Commands", type=CM3D2MENU_PG_CommandPointer, options=PROP_OPTS)
449    active_index = bpy.props.IntProperty(name="Active Command Index", options=PROP_OPTS, default=0)
450    
451    # NOTE : This dictionary is generated by @generate_command_type_collections
452    #command_type_collections = {
453    #    'CM3D2MenuAttachPointCommand'   : 'attach_point_commands',
454    #    'CM3D2MenuPropertyCommand'      : 'property_commands'    ,
455    #    ...
456    #    for all Collection Properties (except 'commands')
457    #}
458
459    updated = bpy.props.BoolProperty(options={'HIDDEN', 'SKIP_SAVE'}, default=False)
460    def update(self):
461        for index, command_pointer in enumerate(self.commands):
462            command = command_pointer.dereference(self)
463            command.index = index
464        updated = True
465
466    def get_active_command(self):
467        if len(self.commands) <= self.active_index:
468            return None
469        command_pointer = self.commands[self.active_index]
470        return command_pointer.dereference(self)
471
472    def new_command(self, command: str):
473        command_type = COMMAND_TYPE_LIST.get(command)
474        collection_name = 'misc_commands'
475        if command_type:
476            collection_name = self.command_type_collections.get(command_type.bl_idname) or collection_name
477        
478        collection = getattr(self, collection_name)
479        new_command = collection.add()
480        new_command.command = command
481        
482        new_pointer = self.commands.add()
483        new_pointer.collection_name = collection_name
484        new_pointer.prop_index = len(collection) - 1
485
486        new_command.index = len(self.commands) - 1
487
488        return new_command
489
490    def remove_command(self, index: int):
491        command_pointer = self.commands[index]
492        command = command_pointer.dereference(self)
493        
494        collection_name = self.command_type_collections.get(command.bl_idname) or 'misc_commands'
495        collection = getattr(self, collection_name)
496
497        prop_index = command_pointer.prop_index
498        self.commands.remove(index)
499        self.update()
500        collection.remove(prop_index)
501
502        for i, c in enumerate(collection):
503            self.commands[c.index].prop_index = i
504
505        if self.active_index >= len(self.commands):
506            self.active_index = len(self.commands) - 1
507
508    def move_command(self, old_index, new_index, update=True):
509        self.commands.move(old_index, new_index)
510        self.updated = False
511        if update:
512            self.update()
513
514    def parse_list(self, string_list):
515        command = string_list[0]
516        new_command = self.new_command(command)
517        new_command.parse_list(string_list)
518
519    def unpack_from_file(self, file):
520        if common.read_str(file) != 'CM3D2_MENU':
521            raise IOError("Not a valid CM3D2 .menu file.")
522
523        self.version      = struct.unpack('<i', file.read(4))[0]
524        self.path         = common.read_str(file)
525        self.name         = common.read_str(file)
526        self.category     = common.read_str(file)
527        self.description  = common.read_str(file)
528        
529        struct.unpack('<i', file.read(4))[0]
530        string_list = []
531        string_list_length = struct.unpack('<B', file.read(1))[0]
532        while string_list_length > 0:
533            string_list.clear()
534
535            for i in range(string_list_length):
536                string_list.append(common.read_str(file))
537            
538            try:
539                self.parse_list(string_list)
540            except ValueError as e:
541                print(e)
542            
543            # Check for end of file
544            chunk = file.read(1)
545            if len(chunk) == 0:
546                break
547            string_list_length = struct.unpack('<B', chunk)[0]
548
549        self.update()
550    
551    def pack_into_file(self, file):
552        self.update()
553
554        common.write_str(file, 'CM3D2_MENU')
555
556        file.write(struct.pack('<i', self.version    ))
557        common.write_str(file,       self.path       )
558        common.write_str(file,       self.name       )
559        common.write_str(file,       self.category   )
560        common.write_str(file,       self.description)
561                    
562        buffer = bytearray()
563        for command_pointer in self.commands:
564            buffer = command_pointer.dereference(self).pack_into(buffer)
565        buffer = buffer + struct.pack('<B', 0x00)
566        
567        file.write(struct.pack('<i', len(buffer)))
568        file.write(bytes(buffer))
569
570    def clear(self):
571        self.property_unset('version'    )
572        self.property_unset('path'       )
573        self.property_unset('name'       )
574        self.property_unset('category'   )
575        self.property_unset('description')
576        
577        for prop in self.command_type_collections.values():
578            self.property_unset(prop)
579
580        self.property_unset('misc_commands')
581
582        self.property_unset('commands'    )
583        self.property_unset('active_index')
584
585        self.property_unset('updated')
586
587
588# Pannel & operators in ./CM3D2 Converter/menu_OBJECT_PT_cm3d2_menu.py
589from . import menu_OBJECT_PT_cm3d2_menu
590
591# メニューを登録する関数
592def import_menu_func(self, context):
593    self.layout.operator(menu_OBJECT_PT_cm3d2_menu.CM3D2MENU_OT_import.bl_idname, text="CM3D2 Menu (.menu)", icon_value=common.kiss_icon())
594
595# メニューを登録する関数
596def export_menu_func(self, context):
597    self.layout.operator(menu_OBJECT_PT_cm3d2_menu.CM3D2MENU_OT_export.bl_idname, text="CM3D2 Menu (.menu)", icon_value=common.kiss_icon())
PROP_OPTS = {'LIBRARY_EDITABLE'}
COMMAND_ENUMS = [('', 'Menu Meta', ''), ('end', 'End', 'description', 'X', 0), ('name', 'Menu Name', 'description', 'FILE_TEXT', 3), ('saveitem', 'Menu Category', 'description', 'FILE_TEXT', 4), ('setumei', 'Menu Description', 'description', 'FILE_TEXT', 5), ('priority', 'Priority', 'description', 'SORT_DESC', 6), ('メニューフォルダ', 'Folder', 'description', 'FILE_FOLDER', 7), ('icon', 'Icon', 'description', 'FILE_IMAGE', 10), ('icons', 'Icon (Small)', 'description', 'FILE_IMAGE', 11), ('iconl', 'Icon (Large)', 'Unused', 'BLANK1', 12), ('', 'Item Meta', ''), ('ver', 'Item Version', 'description', 'FILE_TEXT', 20), ('category', 'Item Category', 'description', 'SORTALPHA', 21), ('catno', 'Item Category Number', 'description', 'LINENUMBERS_ON', 22), ('アイテム', 'Item', 'description', 'FILE_3D', 30), ('アイテム条件', 'Item Conditions', 'description', 'SCRIPTPLUGINS', 31), ('if', 'Item If', 'description', 'FILE_SCRIPT', 32), ('アイテムパラメータ', 'Item Parameters', 'description', 'SCRIPTPLUGINS', 33), ('半脱ぎ', 'Item Half Off', 'description', 'LIBRARY_DATA_INDIRECT', 34), ('リソース参照', 'Item Resource Reference', 'description', 'LIBRARY_DATA_INDIRECT', 35), ('', 'Item Control', ''), ('set', 'Set', 'Unused', 'BLANK1', 40), ('setname', 'Set Name', 'Unused', 'BLANK1', 41), ('setslotitem', 'Set Slot Item', 'description', 'FILE_TICK', 42), ('additem', 'Add Item', 'description', 'ADD', 43), ('unsetitem', 'Unset Item', 'description', 'REMOVE', 44), ('nofloory', 'Disable Item Floor', 'description', 'CON_FLOOR', 45), ('maskitem', 'Mask Item', 'description', 'MOD_MASK', 46), ('delitem', 'Delete Item', 'description', 'TRASH', 47), ('node消去', 'Node Hide', 'description', 'HIDE_ON', 50), ('node表示', 'Node Display', 'description', 'HIDE_OFF', 51), ('パーツnode消去', 'Parts-Node Hide', 'description', 'VIS_SEL_01', 52), ('パーツnode表示', 'Parts-Node Display', 'description', 'VIS_SEL_11', 53), ('', 'Material Control', ''), ('color', 'Color', 'description', 'COLOR', 60), ('mancolor', 'Man Color', 'description', 'GHOST_ENABLED', 61), ('color_set', 'Color-Set', 'description', 'GROUP_VCOL', 62), ('tex', 'Texture', 'description', 'TEXTURE', 70), ('テクスチャ変更', 'Texture Change', 'description', 'TEXTURE', 71), ('テクスチャ乗算', 'Texture Multiplication', 'description', 'FORCE_TEXTURE', 72), ('テクスチャ合成', 'Texture Composition', 'description', 'NODE_TEXTURE', 73), ('テクスチャセット合成', 'Texture Set Composition', 'description', 'NODE_TEXTURE', 74), ('マテリアル変更', 'Material Change', 'description', 'MATERIAL', 80), ('useredit', 'Material Properties', 'description', 'MATERIAL', 81), ('shader', 'Shader', 'description', 'SHADING_RENDERED', 90), ('', 'Maid Control', ''), ('prop', 'Property', 'description', 'PROPERTIES', 100), ('アタッチポイントの設定', 'Attach Point', 'description', 'HOOK', 110), ('blendset', 'Face Blend-Set', 'description', 'SHAPEKEY_DATA', 120), ('paramset', 'Face Parameter-Set', 'description', 'OPTIONS', 121), ('commenttype', 'Profile Comment Type', 'description', 'TEXT', 130), ('bonemorph', 'Bone Morph', 'description', 'CONSTRAINT_BONE', 140), ('length', 'Hair Length', 'description', 'CONSTRAINT_BONE', 141), ('anime', 'Animation', 'description', 'ANIM', 150), ('animematerial', 'Animation (Material)', 'description', 'ANIM', 151), ('param2', 'Parameter 2', 'description', 'CON_TRANSFORM', 160), ('', 'Misc.', ''), ('setstr', 'Set String', 'Unused', 'BLANK1', 170), ('onclickmenu', 'onclickmenu', 'Decorative', 'NONE', 200), ('属性追加', 'addattribute', 'Decorative', 'NONE', 201)]
COMMAND_TYPE_LIST = {'アタッチポイントの設定': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_AttachPointCommand'>, 'prop': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_PropertyCommand'>}
def get_command_enum_info( enum_string, enum_items=[('', 'Menu Meta', ''), ('end', 'End', 'description', 'X', 0), ('name', 'Menu Name', 'description', 'FILE_TEXT', 3), ('saveitem', 'Menu Category', 'description', 'FILE_TEXT', 4), ('setumei', 'Menu Description', 'description', 'FILE_TEXT', 5), ('priority', 'Priority', 'description', 'SORT_DESC', 6), ('メニューフォルダ', 'Folder', 'description', 'FILE_FOLDER', 7), ('icon', 'Icon', 'description', 'FILE_IMAGE', 10), ('icons', 'Icon (Small)', 'description', 'FILE_IMAGE', 11), ('iconl', 'Icon (Large)', 'Unused', 'BLANK1', 12), ('', 'Item Meta', ''), ('ver', 'Item Version', 'description', 'FILE_TEXT', 20), ('category', 'Item Category', 'description', 'SORTALPHA', 21), ('catno', 'Item Category Number', 'description', 'LINENUMBERS_ON', 22), ('アイテム', 'Item', 'description', 'FILE_3D', 30), ('アイテム条件', 'Item Conditions', 'description', 'SCRIPTPLUGINS', 31), ('if', 'Item If', 'description', 'FILE_SCRIPT', 32), ('アイテムパラメータ', 'Item Parameters', 'description', 'SCRIPTPLUGINS', 33), ('半脱ぎ', 'Item Half Off', 'description', 'LIBRARY_DATA_INDIRECT', 34), ('リソース参照', 'Item Resource Reference', 'description', 'LIBRARY_DATA_INDIRECT', 35), ('', 'Item Control', ''), ('set', 'Set', 'Unused', 'BLANK1', 40), ('setname', 'Set Name', 'Unused', 'BLANK1', 41), ('setslotitem', 'Set Slot Item', 'description', 'FILE_TICK', 42), ('additem', 'Add Item', 'description', 'ADD', 43), ('unsetitem', 'Unset Item', 'description', 'REMOVE', 44), ('nofloory', 'Disable Item Floor', 'description', 'CON_FLOOR', 45), ('maskitem', 'Mask Item', 'description', 'MOD_MASK', 46), ('delitem', 'Delete Item', 'description', 'TRASH', 47), ('node消去', 'Node Hide', 'description', 'HIDE_ON', 50), ('node表示', 'Node Display', 'description', 'HIDE_OFF', 51), ('パーツnode消去', 'Parts-Node Hide', 'description', 'VIS_SEL_01', 52), ('パーツnode表示', 'Parts-Node Display', 'description', 'VIS_SEL_11', 53), ('', 'Material Control', ''), ('color', 'Color', 'description', 'COLOR', 60), ('mancolor', 'Man Color', 'description', 'GHOST_ENABLED', 61), ('color_set', 'Color-Set', 'description', 'GROUP_VCOL', 62), ('tex', 'Texture', 'description', 'TEXTURE', 70), ('テクスチャ変更', 'Texture Change', 'description', 'TEXTURE', 71), ('テクスチャ乗算', 'Texture Multiplication', 'description', 'FORCE_TEXTURE', 72), ('テクスチャ合成', 'Texture Composition', 'description', 'NODE_TEXTURE', 73), ('テクスチャセット合成', 'Texture Set Composition', 'description', 'NODE_TEXTURE', 74), ('マテリアル変更', 'Material Change', 'description', 'MATERIAL', 80), ('useredit', 'Material Properties', 'description', 'MATERIAL', 81), ('shader', 'Shader', 'description', 'SHADING_RENDERED', 90), ('', 'Maid Control', ''), ('prop', 'Property', 'description', 'PROPERTIES', 100), ('アタッチポイントの設定', 'Attach Point', 'description', 'HOOK', 110), ('blendset', 'Face Blend-Set', 'description', 'SHAPEKEY_DATA', 120), ('paramset', 'Face Parameter-Set', 'description', 'OPTIONS', 121), ('commenttype', 'Profile Comment Type', 'description', 'TEXT', 130), ('bonemorph', 'Bone Morph', 'description', 'CONSTRAINT_BONE', 140), ('length', 'Hair Length', 'description', 'CONSTRAINT_BONE', 141), ('anime', 'Animation', 'description', 'ANIM', 150), ('animematerial', 'Animation (Material)', 'description', 'ANIM', 151), ('param2', 'Parameter 2', 'description', 'CON_TRANSFORM', 160), ('', 'Misc.', ''), ('setstr', 'Set String', 'Unused', 'BLANK1', 170), ('onclickmenu', 'onclickmenu', 'Decorative', 'NONE', 200), ('属性追加', 'addattribute', 'Decorative', 'NONE', 201)]):
90def get_command_enum_info(enum_string, enum_items=COMMAND_ENUMS):
91    if enum_string == '':
92        return None
93    for enum_info in enum_items:
94        if enum_info[0] == enum_string:
95            return enum_info
96    return None
def get_command_enum_name( enum_string, enum_items=[('', 'Menu Meta', ''), ('end', 'End', 'description', 'X', 0), ('name', 'Menu Name', 'description', 'FILE_TEXT', 3), ('saveitem', 'Menu Category', 'description', 'FILE_TEXT', 4), ('setumei', 'Menu Description', 'description', 'FILE_TEXT', 5), ('priority', 'Priority', 'description', 'SORT_DESC', 6), ('メニューフォルダ', 'Folder', 'description', 'FILE_FOLDER', 7), ('icon', 'Icon', 'description', 'FILE_IMAGE', 10), ('icons', 'Icon (Small)', 'description', 'FILE_IMAGE', 11), ('iconl', 'Icon (Large)', 'Unused', 'BLANK1', 12), ('', 'Item Meta', ''), ('ver', 'Item Version', 'description', 'FILE_TEXT', 20), ('category', 'Item Category', 'description', 'SORTALPHA', 21), ('catno', 'Item Category Number', 'description', 'LINENUMBERS_ON', 22), ('アイテム', 'Item', 'description', 'FILE_3D', 30), ('アイテム条件', 'Item Conditions', 'description', 'SCRIPTPLUGINS', 31), ('if', 'Item If', 'description', 'FILE_SCRIPT', 32), ('アイテムパラメータ', 'Item Parameters', 'description', 'SCRIPTPLUGINS', 33), ('半脱ぎ', 'Item Half Off', 'description', 'LIBRARY_DATA_INDIRECT', 34), ('リソース参照', 'Item Resource Reference', 'description', 'LIBRARY_DATA_INDIRECT', 35), ('', 'Item Control', ''), ('set', 'Set', 'Unused', 'BLANK1', 40), ('setname', 'Set Name', 'Unused', 'BLANK1', 41), ('setslotitem', 'Set Slot Item', 'description', 'FILE_TICK', 42), ('additem', 'Add Item', 'description', 'ADD', 43), ('unsetitem', 'Unset Item', 'description', 'REMOVE', 44), ('nofloory', 'Disable Item Floor', 'description', 'CON_FLOOR', 45), ('maskitem', 'Mask Item', 'description', 'MOD_MASK', 46), ('delitem', 'Delete Item', 'description', 'TRASH', 47), ('node消去', 'Node Hide', 'description', 'HIDE_ON', 50), ('node表示', 'Node Display', 'description', 'HIDE_OFF', 51), ('パーツnode消去', 'Parts-Node Hide', 'description', 'VIS_SEL_01', 52), ('パーツnode表示', 'Parts-Node Display', 'description', 'VIS_SEL_11', 53), ('', 'Material Control', ''), ('color', 'Color', 'description', 'COLOR', 60), ('mancolor', 'Man Color', 'description', 'GHOST_ENABLED', 61), ('color_set', 'Color-Set', 'description', 'GROUP_VCOL', 62), ('tex', 'Texture', 'description', 'TEXTURE', 70), ('テクスチャ変更', 'Texture Change', 'description', 'TEXTURE', 71), ('テクスチャ乗算', 'Texture Multiplication', 'description', 'FORCE_TEXTURE', 72), ('テクスチャ合成', 'Texture Composition', 'description', 'NODE_TEXTURE', 73), ('テクスチャセット合成', 'Texture Set Composition', 'description', 'NODE_TEXTURE', 74), ('マテリアル変更', 'Material Change', 'description', 'MATERIAL', 80), ('useredit', 'Material Properties', 'description', 'MATERIAL', 81), ('shader', 'Shader', 'description', 'SHADING_RENDERED', 90), ('', 'Maid Control', ''), ('prop', 'Property', 'description', 'PROPERTIES', 100), ('アタッチポイントの設定', 'Attach Point', 'description', 'HOOK', 110), ('blendset', 'Face Blend-Set', 'description', 'SHAPEKEY_DATA', 120), ('paramset', 'Face Parameter-Set', 'description', 'OPTIONS', 121), ('commenttype', 'Profile Comment Type', 'description', 'TEXT', 130), ('bonemorph', 'Bone Morph', 'description', 'CONSTRAINT_BONE', 140), ('length', 'Hair Length', 'description', 'CONSTRAINT_BONE', 141), ('anime', 'Animation', 'description', 'ANIM', 150), ('animematerial', 'Animation (Material)', 'description', 'ANIM', 151), ('param2', 'Parameter 2', 'description', 'CON_TRANSFORM', 160), ('', 'Misc.', ''), ('setstr', 'Set String', 'Unused', 'BLANK1', 170), ('onclickmenu', 'onclickmenu', 'Decorative', 'NONE', 200), ('属性追加', 'addattribute', 'Decorative', 'NONE', 201)]):
 98def get_command_enum_name(enum_string, enum_items=COMMAND_ENUMS):
 99    if enum_string == '':
100        return ''
101    for enum_info in enum_items:
102        if enum_info[0] == enum_string:
103            return enum_info[1]
104    return enum_string
@compat.BlRegister()
class CM3D2MENU_PG_CommandPointer(bpy_types.PropertyGroup):
111@compat.BlRegister()
112class CM3D2MENU_PG_CommandPointer(bpy.types.PropertyGroup):
113    bl_idname = 'CM3D2MenuCommandPointer'
114
115    collection_name  = bpy.props.StringProperty(options={'HIDDEN'})
116    prop_index       = bpy.props.IntProperty   (options={'HIDDEN'})
117
118    def dereference(self, data):
119        return getattr(data, self.collection_name)[self.prop_index]
bl_idname = 'CM3D2MenuCommandPointer'
collection_name: <_PropertyDeferred, <built-in function StringProperty>, {'options': {'HIDDEN'}, 'attr': 'collection_name'}> = <_PropertyDeferred, <built-in function StringProperty>, {'options': {'HIDDEN'}, 'attr': 'collection_name'}>
prop_index: <_PropertyDeferred, <built-in function IntProperty>, {'options': {'HIDDEN'}, 'attr': 'prop_index'}> = <_PropertyDeferred, <built-in function IntProperty>, {'options': {'HIDDEN'}, 'attr': 'prop_index'}>
def dereference(self, data):
118    def dereference(self, data):
119        return getattr(data, self.collection_name)[self.prop_index]
bl_rna = <bpy_struct, Struct("CM3D2MENU_PG_CommandPointer")>
Inherited Members
builtins.bpy_struct
keys
values
items
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
@compat.BlRegister()
class MISCCOMMAND_PG_Param(bpy_types.PropertyGroup):
122@compat.BlRegister()
123class MISCCOMMAND_PG_Param(bpy.types.PropertyGroup):
124    bl_idname = 'CM3D2MenuParam'
125
126    # Really the value should be saved, not the name, but template_list() doesn't like that so they're switched.
127    def _s(self, value):
128        self.name = value
129
130    #name = bpy.props.StringProperty(name="Name", options=PROP_OPTS, get=lambda self : self.value)
131    name   = bpy.props.StringProperty(name="Name", default="param", options={'HIDDEN'})
132    value  = bpy.props.StringProperty(name="Slot Name", options={'SKIP_SAVE'}, default="param", set=_s, get=lambda self: self.name)
bl_idname = 'CM3D2MenuParam'
name: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'default': 'param', 'options': {'HIDDEN'}, 'attr': 'name'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'default': 'param', 'options': {'HIDDEN'}, 'attr': 'name'}>
value: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Slot Name', 'options': {'SKIP_SAVE'}, 'default': 'param', 'set': <function MISCCOMMAND_PG_Param._s at 0x7f6a0eb4dab0>, 'get': <function MISCCOMMAND_PG_Param.<lambda> at 0x7f6a0eb4db40>, 'attr': 'value'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Slot Name', 'options': {'SKIP_SAVE'}, 'default': 'param', 'set': <function MISCCOMMAND_PG_Param._s>, 'get': <function MISCCOMMAND_PG_Param.<lambda>>, 'attr': 'value'}>
bl_rna = <bpy_struct, Struct("MISCCOMMAND_PG_Param")>
Inherited Members
builtins.bpy_struct
keys
values
items
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
class CM3D2MenuCommand:
138class CM3D2MenuCommand():
139    def __init__(self, *args, name="{command_name}"):
140        self.name_template = name
141        self.command_enums = set()
142        for command in args:
143            for enum_info in COMMAND_ENUMS:
144                if enum_info[0] == command:
145                    self.command_enums.add(enum_info)
146                    break
147            continue
148    
149    def __call__(self, cls):
150        cls.name_template = self.name_template
151        cls.command_enums = self.command_enums
152        # Associate the class with its command in COMMAND_CLASSES
153        for enum in cls.command_enums:
154            COMMAND_TYPE_LIST[enum[0]] = cls
155
156        # define command property
157        if len(cls.command_enums) > 0:
158            cls.command = bpy.props.EnumProperty(
159                items   = cls.command_enums,
160                name    = "Command",
161                options = PROP_OPTS,
162                default = next(iter(cls.command_enums))[0],
163                description = "The command of this menu file command-chunk"
164            )
165        else:
166            cls.command = bpy.props.StringProperty(
167                name    = "Command",
168                options = PROP_OPTS,
169                default = "command",
170                description = "The command of this menu file command-chunk"
171            )
172
173        cls.index = bpy.props.IntProperty(name="Index", options=PROP_OPTS)
174        #cls.initalized = bpy.props.BoolProperty(name="Index", options={'SKIP_SAVE'}, default=False)
175
176        attributes = dir(cls)
177        cls.name_format_attributes = set()
178        for attr in attributes:
179            if "{"+attr+"}" in cls.name_template:
180                cls.name_format_attributes.add(attr)
181
182        def format_name(self):
183            params = { attr : getattr(self, attr) for attr in cls.name_format_attributes }
184            if len(cls.command_enums) > 0:
185                params['command_name'] = get_command_enum_name(self.command, enum_items=cls.command_enums)
186            else:
187                params['command_name'] = get_command_enum_name(self.command)
188            return cls.name_template.format(**params)
189        
190        cls.format_name = format_name
191        
192        # define name property
193        cls.name = bpy.props.StringProperty(
194            name    = "Name"                 ,
195            options = {'HIDDEN', 'SKIP_SAVE'},
196            get     = cls.format_name
197        )
198
199        # add catch and rethrow for functions
200        def catch_throw_wrap(func, gerund, catch_type, throw_type=None):
201            throw_type = throw_type or catch_type
202            prefix = f_tip_("Error {gerund} {bl_idname}: ", gerund=gerund, bl_idname=cls.bl_idname) + "{message}"
203            def _f(*args, **kwargs):
204                try:
205                    return func(*args, **kwargs)
206                except catch_type as e:
207                    raise throw_type(prefix.format(message=e.args[0]))
208            return _f
209
210        cls.parse_list = catch_throw_wrap(cls.parse_list, "parsing", ValueError)
211        cls.pack_into  = catch_throw_wrap(cls.pack_into , "packing", ValueError)
212
213        return cls
CM3D2MenuCommand(*args, name='{command_name}')
139    def __init__(self, *args, name="{command_name}"):
140        self.name_template = name
141        self.command_enums = set()
142        for command in args:
143            for enum_info in COMMAND_ENUMS:
144                if enum_info[0] == command:
145                    self.command_enums.add(enum_info)
146                    break
147            continue
name_template
command_enums
@compat.BlRegister()
@CM3D2MenuCommand('アタッチポイントの設定', name='{command_name} : {point_name}')
class CM3D2MENU_PG_AttachPointCommand(bpy_types.PropertyGroup):
220@compat.BlRegister()
221@CM3D2MenuCommand('アタッチポイントの設定', name="{command_name} : {point_name}")
222class CM3D2MENU_PG_AttachPointCommand(bpy.types.PropertyGroup):
223    bl_idname = 'CM3D2MenuAttachPointCommand'
224    '''
225    アタッチポイントの設定
226      ├ point_name(呼び出し名)
227      ├ location.x(座標)
228      ├ location.y(座標)
229      ├ location.z(座標)
230      ├ rotation.x(軸回転角度)[範囲:0±180°]
231      ├ rotation.y(軸回転角度)[範囲:0±180°]
232      └ rotation.z(軸回転角度)[範囲:0±180°]
233    '''
234    point_name = bpy.props.StringProperty     (name="Point Name", default="Attach Point", description="Name of the slot to define the attatchment point for" , options=PROP_OPTS)
235    location   = bpy.props.FloatVectorProperty(name="Location"  , default=(0, 0, 0)     , description="Location of the attatchment relative to the base bone", options=PROP_OPTS, subtype=compat.subtype('TRANSLATION'))
236    rotation   = bpy.props.FloatVectorProperty(name="Rotation"  , default=(0, 0, 0)     , description="Rotation of the attatchment relative to the base bone", options=PROP_OPTS, subtype=compat.subtype('EULER'      ))
237
238    def parse_list(self, string_list):
239        self.command = string_list[0]
240        self.point_name  = string_list[1]
241        self.location.x = float(string_list[2])
242        self.location.y = float(string_list[3])
243        self.location.z = float(string_list[4])
244        self.rotation.x = float(string_list[5]) * math.pi/180
245        self.rotation.y = float(string_list[6]) * math.pi/180
246        self.rotation.z = float(string_list[7]) * math.pi/180
247    
248    def pack_into(self, buffer):
249        buffer = buffer + struct.pack('<B', 1 + 1 + 3 + 3)
250        buffer = common.pack_str(buffer, self.command   )
251        buffer = common.pack_str(buffer, self.point_name)
252        buffer = common.pack_str(buffer, str(self.location.x)              )
253        buffer = common.pack_str(buffer, str(self.location.y)              )
254        buffer = common.pack_str(buffer, str(self.location.z)              )
255        buffer = common.pack_str(buffer, str(self.rotation.x * 180/math.pi))
256        buffer = common.pack_str(buffer, str(self.rotation.y * 180/math.pi))
257        buffer = common.pack_str(buffer, str(self.rotation.z * 180/math.pi))
258
259        return buffer
260
261    def draw(self, context, layout):
262        layout.label(text=self.name)
263
264        col = layout.column()
265        col.alignment = 'RIGHT'
266        col.prop(self, 'command', translate=False)
267        col.label(text=self.command + "     ", translate=False)
268
269        col = layout.column()
270        col.prop(self, 'point_name')
271        col.prop(self, 'location'  )
272        col.prop(self, 'rotation'  )
273        
274        col = layout.column(align=True)
275        col.operator('cm3d2menu.align_selected_to_attach_point', icon=compat.icon('OBJECT_ORIGIN')    )
276        col.operator('cm3d2menu.align_attach_point_to_selected', icon=compat.icon('ORIENTATION_LOCAL'))
bl_idname = 'CM3D2MenuAttachPointCommand'

アタッチポイントの設定 ├ point_name(呼び出し名) ├ location.x(座標) ├ location.y(座標) ├ location.z(座標) ├ rotation.x(軸回転角度)[範囲:0±180°] ├ rotation.y(軸回転角度)[範囲:0±180°] └ rotation.z(軸回転角度)[範囲:0±180°]

point_name: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Point Name', 'default': 'Attach Point', 'description': 'Name of the slot to define the attatchment point for', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'point_name'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Point Name', 'default': 'Attach Point', 'description': 'Name of the slot to define the attatchment point for', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'point_name'}>
location: <_PropertyDeferred, <built-in function FloatVectorProperty>, {'name': 'Location', 'default': (0, 0, 0), 'description': 'Location of the attatchment relative to the base bone', 'options': {'LIBRARY_EDITABLE'}, 'subtype': 'TRANSLATION', 'attr': 'location'}> = <_PropertyDeferred, <built-in function FloatVectorProperty>, {'name': 'Location', 'default': (0, 0, 0), 'description': 'Location of the attatchment relative to the base bone', 'options': {'LIBRARY_EDITABLE'}, 'subtype': 'TRANSLATION', 'attr': 'location'}>
rotation: <_PropertyDeferred, <built-in function FloatVectorProperty>, {'name': 'Rotation', 'default': (0, 0, 0), 'description': 'Rotation of the attatchment relative to the base bone', 'options': {'LIBRARY_EDITABLE'}, 'subtype': 'EULER', 'attr': 'rotation'}> = <_PropertyDeferred, <built-in function FloatVectorProperty>, {'name': 'Rotation', 'default': (0, 0, 0), 'description': 'Rotation of the attatchment relative to the base bone', 'options': {'LIBRARY_EDITABLE'}, 'subtype': 'EULER', 'attr': 'rotation'}>
def parse_list(*args, **kwargs):
203            def _f(*args, **kwargs):
204                try:
205                    return func(*args, **kwargs)
206                except catch_type as e:
207                    raise throw_type(prefix.format(message=e.args[0]))
def pack_into(*args, **kwargs):
203            def _f(*args, **kwargs):
204                try:
205                    return func(*args, **kwargs)
206                except catch_type as e:
207                    raise throw_type(prefix.format(message=e.args[0]))
def draw(self, context, layout):
261    def draw(self, context, layout):
262        layout.label(text=self.name)
263
264        col = layout.column()
265        col.alignment = 'RIGHT'
266        col.prop(self, 'command', translate=False)
267        col.label(text=self.command + "     ", translate=False)
268
269        col = layout.column()
270        col.prop(self, 'point_name')
271        col.prop(self, 'location'  )
272        col.prop(self, 'rotation'  )
273        
274        col = layout.column(align=True)
275        col.operator('cm3d2menu.align_selected_to_attach_point', icon=compat.icon('OBJECT_ORIGIN')    )
276        col.operator('cm3d2menu.align_attach_point_to_selected', icon=compat.icon('ORIENTATION_LOCAL'))
name_template = '{command_name} : {point_name}'
command_enums = {('アタッチポイントの設定', 'Attach Point', 'description', 'HOOK', 110)}
command: <_PropertyDeferred, <built-in function EnumProperty>, {'items': {('アタッチポイントの設定', 'Attach Point', 'description', 'HOOK', 110)}, 'name': 'Command', 'options': {'LIBRARY_EDITABLE'}, 'default': 'アタッチポイントの設定', 'description': 'The command of this menu file command-chunk', 'attr': 'command'}> = <_PropertyDeferred, <built-in function EnumProperty>, {'items': {('アタッチポイントの設定', 'Attach Point', 'description', 'HOOK', 110)}, 'name': 'Command', 'options': {'LIBRARY_EDITABLE'}, 'default': 'アタッチポイントの設定', 'description': 'The command of this menu file command-chunk', 'attr': 'command'}>
index: <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Index', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'index'}> = <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Index', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'index'}>
name_format_attributes = {'point_name'}
def format_name(self):
182        def format_name(self):
183            params = { attr : getattr(self, attr) for attr in cls.name_format_attributes }
184            if len(cls.command_enums) > 0:
185                params['command_name'] = get_command_enum_name(self.command, enum_items=cls.command_enums)
186            else:
187                params['command_name'] = get_command_enum_name(self.command)
188            return cls.name_template.format(**params)
name: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'options': {'HIDDEN', 'SKIP_SAVE'}, 'get': <function CM3D2MenuCommand.__call__.<locals>.format_name at 0x7f6a0eb4d990>, 'attr': 'name'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'options': {'HIDDEN', 'SKIP_SAVE'}, 'get': <function CM3D2MenuCommand.__call__.<locals>.format_name>, 'attr': 'name'}>
bl_rna = <bpy_struct, Struct("CM3D2MENU_PG_AttachPointCommand")>
Inherited Members
builtins.bpy_struct
keys
values
items
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
@compat.BlRegister()
@CM3D2MenuCommand('prop', name='{command_name} : {prop_name} = {value}')
class CM3D2MENU_PG_PropertyCommand(bpy_types.PropertyGroup):
279@compat.BlRegister()
280@CM3D2MenuCommand('prop', name="{command_name} : {prop_name} = {value}")
281class CM3D2MENU_PG_PropertyCommand(bpy.types.PropertyGroup):
282    bl_idname = 'CM3D2PropertyMenuCommand'
283    '''
284    prop
285      ├ prop_name
286      └ value
287    '''
288    prop_name = bpy.props.StringProperty(name="Property Name" , default="prop name", description="Name of the property to set on load" , options=PROP_OPTS)
289    value     = bpy.props.FloatProperty (name="Property Value", default=50         , description="Value of the property to set on load", options=PROP_OPTS)
290    
291    def parse_list(self, string_list):
292        self.command    = string_list[0]
293        self.prop_name  = string_list[1]
294        self.value      = float(string_list[2])
295    
296    def pack_into(self, buffer):
297        buffer = buffer + struct.pack('<B', 1 + 1 + 1)
298        buffer = common.pack_str(buffer, self.command    )
299        buffer = common.pack_str(buffer, self.prop_name  )
300        buffer = common.pack_str(buffer, str(self.value) )
301
302        return buffer
303
304    def draw(self, context, layout):
305        col = layout.column()
306        col.alignment = 'RIGHT'
307        col.prop(self, 'command', translate=False)
308        col.label(text=self.command + "     ", translate=False)
309
310        col = layout.column()
311        col.label(text=self.command, translate=False)
312        col.prop(self, 'prop_name')
313        col.prop(self, 'value'    )
bl_idname = 'CM3D2PropertyMenuCommand'

prop ├ prop_name └ value

prop_name: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Property Name', 'default': 'prop name', 'description': 'Name of the property to set on load', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'prop_name'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Property Name', 'default': 'prop name', 'description': 'Name of the property to set on load', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'prop_name'}>
value: <_PropertyDeferred, <built-in function FloatProperty>, {'name': 'Property Value', 'default': 50, 'description': 'Value of the property to set on load', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'value'}> = <_PropertyDeferred, <built-in function FloatProperty>, {'name': 'Property Value', 'default': 50, 'description': 'Value of the property to set on load', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'value'}>
def parse_list(*args, **kwargs):
203            def _f(*args, **kwargs):
204                try:
205                    return func(*args, **kwargs)
206                except catch_type as e:
207                    raise throw_type(prefix.format(message=e.args[0]))
def pack_into(*args, **kwargs):
203            def _f(*args, **kwargs):
204                try:
205                    return func(*args, **kwargs)
206                except catch_type as e:
207                    raise throw_type(prefix.format(message=e.args[0]))
def draw(self, context, layout):
304    def draw(self, context, layout):
305        col = layout.column()
306        col.alignment = 'RIGHT'
307        col.prop(self, 'command', translate=False)
308        col.label(text=self.command + "     ", translate=False)
309
310        col = layout.column()
311        col.label(text=self.command, translate=False)
312        col.prop(self, 'prop_name')
313        col.prop(self, 'value'    )
name_template = '{command_name} : {prop_name} = {value}'
command_enums = {('prop', 'Property', 'description', 'PROPERTIES', 100)}
command: <_PropertyDeferred, <built-in function EnumProperty>, {'items': {('prop', 'Property', 'description', 'PROPERTIES', 100)}, 'name': 'Command', 'options': {'LIBRARY_EDITABLE'}, 'default': 'prop', 'description': 'The command of this menu file command-chunk', 'attr': 'command'}> = <_PropertyDeferred, <built-in function EnumProperty>, {'items': {('prop', 'Property', 'description', 'PROPERTIES', 100)}, 'name': 'Command', 'options': {'LIBRARY_EDITABLE'}, 'default': 'prop', 'description': 'The command of this menu file command-chunk', 'attr': 'command'}>
index: <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Index', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'index'}> = <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Index', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'index'}>
name_format_attributes = {'value', 'prop_name'}
def format_name(self):
182        def format_name(self):
183            params = { attr : getattr(self, attr) for attr in cls.name_format_attributes }
184            if len(cls.command_enums) > 0:
185                params['command_name'] = get_command_enum_name(self.command, enum_items=cls.command_enums)
186            else:
187                params['command_name'] = get_command_enum_name(self.command)
188            return cls.name_template.format(**params)
name: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'options': {'HIDDEN', 'SKIP_SAVE'}, 'get': <function CM3D2MenuCommand.__call__.<locals>.format_name at 0x7f6a0eb4dea0>, 'attr': 'name'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'options': {'HIDDEN', 'SKIP_SAVE'}, 'get': <function CM3D2MenuCommand.__call__.<locals>.format_name>, 'attr': 'name'}>
bl_rna = <bpy_struct, Struct("CM3D2MENU_PG_PropertyCommand")>
Inherited Members
builtins.bpy_struct
keys
values
items
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
@compat.BlRegister()
@CM3D2MenuCommand(name='{command_name}')
class CM3D2MENU_PG_MiscCommand(bpy_types.PropertyGroup):
316@compat.BlRegister()
317@CM3D2MenuCommand(name="{command_name}")
318class CM3D2MENU_PG_MiscCommand(bpy.types.PropertyGroup):
319    bl_idname = 'CM3D2MenuMiscCommand'
320    '''
321    command
322      ├ child_0
323      ├ child_1
324      ├ child_2
325      ├ ...
326      ├ child_n-1
327      └ child_n
328    '''
329    params = bpy.props.CollectionProperty(name="Parameters", options=PROP_OPTS, type=MISCCOMMAND_PG_Param)
330    
331    active_index = bpy.props.IntProperty(options={'HIDDEN'})
332
333    search = bpy.props.BoolProperty(name="Search", default=False, description="Search for suggestions", options=PROP_OPTS)
334
335    def new_param(self):
336        new_param = self.params.add()
337        new_param.value = "newparam"
338        return new_param
339
340    def remove_param(self, index: int):
341        return self.params.remove(index)
342
343    def move_param(self, old_index, new_index):
344        return self.params.move(old_index, new_index)
345
346    def parse_list(self, string_list):
347        self.command = string_list[0]
348        for param in string_list[1:]:
349            new_param = self.params.add()
350            new_param.value = param
351            new_param.name  = param
352
353    def pack_into(self, buffer):
354        buffer = buffer + struct.pack('<B', 1 + len(self.params))
355        buffer = common.pack_str(buffer, self.command)
356        for param in self.params:
357            buffer = common.pack_str(buffer, param.value)
358        return buffer
359    
360    def draw(self, context, layout):
361        enum_info = get_command_enum_info(self.command)
362        if enum_info:
363            layout.label(text=enum_info[1], icon=enum_info[3])
364
365        row = layout.row(align=True)
366        if not compat.IS_LEGACY:
367            row.use_property_split = False
368        if self.search:
369            search_data = bpy.ops.cm3d2menu.command_add.get_rna_type().properties.get('type')
370            row.prop_search(self, 'command', search_data, 'enum_items', text="", translate=True, icon='VIEWZOOM')
371        else:
372            row.prop(self, 'command', text="")
373        row.prop(self, 'search', text='', icon='ZOOM_OUT' if self.search else 'VIEWZOOM')
374        
375        
376        row = layout.row()
377        row.template_list('UI_UL_list', 'CM3D2MENU_UL_misc_command_children',
378            self, 'params'      ,
379            self, 'active_index',
380            rows    = 3,
381            maxrows = 8,
382        )
383        sub_col = row.column(align=True)
384        sub_col.operator('cm3d2menu.param_add'   , icon='ADD'   , text="")
385        sub_col.operator('cm3d2menu.param_remove', icon='REMOVE', text="")
386        #sub_col.separator()
387        #sub_col.menu("OBJECT_MT_cm3d2_menu_context_menu", icon='DOWNARROW_HLT', text="")
388        if self.active_index < len(self.params):
389            sub_col.separator()
390            sub_col.operator("cm3d2menu.param_move", icon='TRIA_UP'  , text="").direction = 'UP'  
391            sub_col.operator("cm3d2menu.param_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
bl_idname = 'CM3D2MenuMiscCommand'

command ├ child_0 ├ child_1 ├ child_2 ├ ... ├ child_n-1 └ child_n

params: <_PropertyDeferred, <built-in function CollectionProperty>, {'name': 'Parameters', 'options': {'LIBRARY_EDITABLE'}, 'type': <class 'CM3D2 Converter.menu_file.MISCCOMMAND_PG_Param'>, 'attr': 'params'}> = <_PropertyDeferred, <built-in function CollectionProperty>, {'name': 'Parameters', 'options': {'LIBRARY_EDITABLE'}, 'type': <class 'CM3D2 Converter.menu_file.MISCCOMMAND_PG_Param'>, 'attr': 'params'}>
active_index: <_PropertyDeferred, <built-in function IntProperty>, {'options': {'HIDDEN'}, 'attr': 'active_index'}> = <_PropertyDeferred, <built-in function IntProperty>, {'options': {'HIDDEN'}, 'attr': 'active_index'}>
search: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Search', 'default': False, 'description': 'Search for suggestions', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'search'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Search', 'default': False, 'description': 'Search for suggestions', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'search'}>
def new_param(self):
335    def new_param(self):
336        new_param = self.params.add()
337        new_param.value = "newparam"
338        return new_param
def remove_param(self, index: int):
340    def remove_param(self, index: int):
341        return self.params.remove(index)
def move_param(self, old_index, new_index):
343    def move_param(self, old_index, new_index):
344        return self.params.move(old_index, new_index)
def parse_list(*args, **kwargs):
203            def _f(*args, **kwargs):
204                try:
205                    return func(*args, **kwargs)
206                except catch_type as e:
207                    raise throw_type(prefix.format(message=e.args[0]))
def pack_into(*args, **kwargs):
203            def _f(*args, **kwargs):
204                try:
205                    return func(*args, **kwargs)
206                except catch_type as e:
207                    raise throw_type(prefix.format(message=e.args[0]))
def draw(self, context, layout):
360    def draw(self, context, layout):
361        enum_info = get_command_enum_info(self.command)
362        if enum_info:
363            layout.label(text=enum_info[1], icon=enum_info[3])
364
365        row = layout.row(align=True)
366        if not compat.IS_LEGACY:
367            row.use_property_split = False
368        if self.search:
369            search_data = bpy.ops.cm3d2menu.command_add.get_rna_type().properties.get('type')
370            row.prop_search(self, 'command', search_data, 'enum_items', text="", translate=True, icon='VIEWZOOM')
371        else:
372            row.prop(self, 'command', text="")
373        row.prop(self, 'search', text='', icon='ZOOM_OUT' if self.search else 'VIEWZOOM')
374        
375        
376        row = layout.row()
377        row.template_list('UI_UL_list', 'CM3D2MENU_UL_misc_command_children',
378            self, 'params'      ,
379            self, 'active_index',
380            rows    = 3,
381            maxrows = 8,
382        )
383        sub_col = row.column(align=True)
384        sub_col.operator('cm3d2menu.param_add'   , icon='ADD'   , text="")
385        sub_col.operator('cm3d2menu.param_remove', icon='REMOVE', text="")
386        #sub_col.separator()
387        #sub_col.menu("OBJECT_MT_cm3d2_menu_context_menu", icon='DOWNARROW_HLT', text="")
388        if self.active_index < len(self.params):
389            sub_col.separator()
390            sub_col.operator("cm3d2menu.param_move", icon='TRIA_UP'  , text="").direction = 'UP'  
391            sub_col.operator("cm3d2menu.param_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
name_template = '{command_name}'
command_enums = set()
command: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Command', 'options': {'LIBRARY_EDITABLE'}, 'default': 'command', 'description': 'The command of this menu file command-chunk', 'attr': 'command'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Command', 'options': {'LIBRARY_EDITABLE'}, 'default': 'command', 'description': 'The command of this menu file command-chunk', 'attr': 'command'}>
index: <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Index', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'index'}> = <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Index', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'index'}>
name_format_attributes = set()
def format_name(self):
182        def format_name(self):
183            params = { attr : getattr(self, attr) for attr in cls.name_format_attributes }
184            if len(cls.command_enums) > 0:
185                params['command_name'] = get_command_enum_name(self.command, enum_items=cls.command_enums)
186            else:
187                params['command_name'] = get_command_enum_name(self.command)
188            return cls.name_template.format(**params)
name: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'options': {'HIDDEN', 'SKIP_SAVE'}, 'get': <function CM3D2MenuCommand.__call__.<locals>.format_name at 0x7f6a0eb4e200>, 'attr': 'name'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'options': {'HIDDEN', 'SKIP_SAVE'}, 'get': <function CM3D2MenuCommand.__call__.<locals>.format_name>, 'attr': 'name'}>
bl_rna = <bpy_struct, Struct("CM3D2MENU_PG_MiscCommand")>
Inherited Members
builtins.bpy_struct
keys
values
items
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
def generate_command_type_collections(cls):
399def generate_command_type_collections(cls):
400    prop_example = bpy.props.CollectionProperty(type=bpy.types.PropertyGroup)
401    cls.command_type_collections = {}
402
403    for prop_name in dir(cls):
404        if prop_name == "commands":
405            continue
406
407        prop = getattr(cls, prop_name)
408        command_class = None
409        if type(prop) != type(prop_example):
410            continue
411        if "len" in dir(prop):
412            if len(prop)  != len(prop_example) :
413                continue
414            if prop[0]    != prop_example[0]   :
415                continue
416            command_class = prop[1]["type"]
417
418        # Fix for Blender 2.93
419        elif "function" in dir(prop) and "keywords" in dir(prop) and "type" in prop.keywords.keys(): 
420            if prop.function != prop_example.function:
421                continue
422            command_class = prop.keywords["type"]
423        else:
424            continue
425
426        if command_class == CM3D2MENU_PG_CommandPointer:
427            continue
428
429        cls.command_type_collections[command_class.bl_idname] = prop_name
430        #print(cls.bl_idname+".command_type_collections[\""+prop_name+"\"]", "=", prop)
431    
432    return cls
@compat.BlRegister()
@generate_command_type_collections
class OBJECT_PG_CM3D2Menu(bpy_types.PropertyGroup):
434@compat.BlRegister()
435@generate_command_type_collections
436class OBJECT_PG_CM3D2Menu(bpy.types.PropertyGroup):
437    bl_idname = 'CM3D2Menu'
438
439    version     = bpy.props.IntProperty   (name="Version"    , options=PROP_OPTS, min=0, step=100    )
440    path        = bpy.props.StringProperty(name="Path"       , options=PROP_OPTS, subtype='FILE_PATH')
441    name        = bpy.props.StringProperty(name="Name"       , options=PROP_OPTS)
442    category    = bpy.props.StringProperty(name="Category"   , options=PROP_OPTS)
443    description = bpy.props.StringProperty(name="Description", options=PROP_OPTS)
444                                                                       
445    attach_point_commands = bpy.props.CollectionProperty(type=CM3D2MENU_PG_AttachPointCommand, options={'HIDDEN'})
446    property_commands     = bpy.props.CollectionProperty(type=CM3D2MENU_PG_PropertyCommand   , options={'HIDDEN'})
447    misc_commands         = bpy.props.CollectionProperty(type=CM3D2MENU_PG_MiscCommand       , options={'HIDDEN'})
448
449    commands = bpy.props.CollectionProperty(name="Commands", type=CM3D2MENU_PG_CommandPointer, options=PROP_OPTS)
450    active_index = bpy.props.IntProperty(name="Active Command Index", options=PROP_OPTS, default=0)
451    
452    # NOTE : This dictionary is generated by @generate_command_type_collections
453    #command_type_collections = {
454    #    'CM3D2MenuAttachPointCommand'   : 'attach_point_commands',
455    #    'CM3D2MenuPropertyCommand'      : 'property_commands'    ,
456    #    ...
457    #    for all Collection Properties (except 'commands')
458    #}
459
460    updated = bpy.props.BoolProperty(options={'HIDDEN', 'SKIP_SAVE'}, default=False)
461    def update(self):
462        for index, command_pointer in enumerate(self.commands):
463            command = command_pointer.dereference(self)
464            command.index = index
465        updated = True
466
467    def get_active_command(self):
468        if len(self.commands) <= self.active_index:
469            return None
470        command_pointer = self.commands[self.active_index]
471        return command_pointer.dereference(self)
472
473    def new_command(self, command: str):
474        command_type = COMMAND_TYPE_LIST.get(command)
475        collection_name = 'misc_commands'
476        if command_type:
477            collection_name = self.command_type_collections.get(command_type.bl_idname) or collection_name
478        
479        collection = getattr(self, collection_name)
480        new_command = collection.add()
481        new_command.command = command
482        
483        new_pointer = self.commands.add()
484        new_pointer.collection_name = collection_name
485        new_pointer.prop_index = len(collection) - 1
486
487        new_command.index = len(self.commands) - 1
488
489        return new_command
490
491    def remove_command(self, index: int):
492        command_pointer = self.commands[index]
493        command = command_pointer.dereference(self)
494        
495        collection_name = self.command_type_collections.get(command.bl_idname) or 'misc_commands'
496        collection = getattr(self, collection_name)
497
498        prop_index = command_pointer.prop_index
499        self.commands.remove(index)
500        self.update()
501        collection.remove(prop_index)
502
503        for i, c in enumerate(collection):
504            self.commands[c.index].prop_index = i
505
506        if self.active_index >= len(self.commands):
507            self.active_index = len(self.commands) - 1
508
509    def move_command(self, old_index, new_index, update=True):
510        self.commands.move(old_index, new_index)
511        self.updated = False
512        if update:
513            self.update()
514
515    def parse_list(self, string_list):
516        command = string_list[0]
517        new_command = self.new_command(command)
518        new_command.parse_list(string_list)
519
520    def unpack_from_file(self, file):
521        if common.read_str(file) != 'CM3D2_MENU':
522            raise IOError("Not a valid CM3D2 .menu file.")
523
524        self.version      = struct.unpack('<i', file.read(4))[0]
525        self.path         = common.read_str(file)
526        self.name         = common.read_str(file)
527        self.category     = common.read_str(file)
528        self.description  = common.read_str(file)
529        
530        struct.unpack('<i', file.read(4))[0]
531        string_list = []
532        string_list_length = struct.unpack('<B', file.read(1))[0]
533        while string_list_length > 0:
534            string_list.clear()
535
536            for i in range(string_list_length):
537                string_list.append(common.read_str(file))
538            
539            try:
540                self.parse_list(string_list)
541            except ValueError as e:
542                print(e)
543            
544            # Check for end of file
545            chunk = file.read(1)
546            if len(chunk) == 0:
547                break
548            string_list_length = struct.unpack('<B', chunk)[0]
549
550        self.update()
551    
552    def pack_into_file(self, file):
553        self.update()
554
555        common.write_str(file, 'CM3D2_MENU')
556
557        file.write(struct.pack('<i', self.version    ))
558        common.write_str(file,       self.path       )
559        common.write_str(file,       self.name       )
560        common.write_str(file,       self.category   )
561        common.write_str(file,       self.description)
562                    
563        buffer = bytearray()
564        for command_pointer in self.commands:
565            buffer = command_pointer.dereference(self).pack_into(buffer)
566        buffer = buffer + struct.pack('<B', 0x00)
567        
568        file.write(struct.pack('<i', len(buffer)))
569        file.write(bytes(buffer))
570
571    def clear(self):
572        self.property_unset('version'    )
573        self.property_unset('path'       )
574        self.property_unset('name'       )
575        self.property_unset('category'   )
576        self.property_unset('description')
577        
578        for prop in self.command_type_collections.values():
579            self.property_unset(prop)
580
581        self.property_unset('misc_commands')
582
583        self.property_unset('commands'    )
584        self.property_unset('active_index')
585
586        self.property_unset('updated')
bl_idname = 'CM3D2Menu'
version: <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Version', 'options': {'LIBRARY_EDITABLE'}, 'min': 0, 'step': 100, 'attr': 'version'}> = <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Version', 'options': {'LIBRARY_EDITABLE'}, 'min': 0, 'step': 100, 'attr': 'version'}>
path: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Path', 'options': {'LIBRARY_EDITABLE'}, 'subtype': 'FILE_PATH', 'attr': 'path'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Path', 'options': {'LIBRARY_EDITABLE'}, 'subtype': 'FILE_PATH', 'attr': 'path'}>
name: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'name'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Name', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'name'}>
category: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Category', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'category'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Category', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'category'}>
description: <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Description', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'description'}> = <_PropertyDeferred, <built-in function StringProperty>, {'name': 'Description', 'options': {'LIBRARY_EDITABLE'}, 'attr': 'description'}>
attach_point_commands: <_PropertyDeferred, <built-in function CollectionProperty>, {'type': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_AttachPointCommand'>, 'options': {'HIDDEN'}, 'attr': 'attach_point_commands'}> = <_PropertyDeferred, <built-in function CollectionProperty>, {'type': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_AttachPointCommand'>, 'options': {'HIDDEN'}, 'attr': 'attach_point_commands'}>
property_commands: <_PropertyDeferred, <built-in function CollectionProperty>, {'type': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_PropertyCommand'>, 'options': {'HIDDEN'}, 'attr': 'property_commands'}> = <_PropertyDeferred, <built-in function CollectionProperty>, {'type': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_PropertyCommand'>, 'options': {'HIDDEN'}, 'attr': 'property_commands'}>
misc_commands: <_PropertyDeferred, <built-in function CollectionProperty>, {'type': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_MiscCommand'>, 'options': {'HIDDEN'}, 'attr': 'misc_commands'}> = <_PropertyDeferred, <built-in function CollectionProperty>, {'type': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_MiscCommand'>, 'options': {'HIDDEN'}, 'attr': 'misc_commands'}>
commands: <_PropertyDeferred, <built-in function CollectionProperty>, {'name': 'Commands', 'type': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_CommandPointer'>, 'options': {'LIBRARY_EDITABLE'}, 'attr': 'commands'}> = <_PropertyDeferred, <built-in function CollectionProperty>, {'name': 'Commands', 'type': <class 'CM3D2 Converter.menu_file.CM3D2MENU_PG_CommandPointer'>, 'options': {'LIBRARY_EDITABLE'}, 'attr': 'commands'}>
active_index: <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Active Command Index', 'options': {'LIBRARY_EDITABLE'}, 'default': 0, 'attr': 'active_index'}> = <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Active Command Index', 'options': {'LIBRARY_EDITABLE'}, 'default': 0, 'attr': 'active_index'}>
updated: <_PropertyDeferred, <built-in function BoolProperty>, {'options': {'HIDDEN', 'SKIP_SAVE'}, 'default': False, 'attr': 'updated'}> = <_PropertyDeferred, <built-in function BoolProperty>, {'options': {'HIDDEN', 'SKIP_SAVE'}, 'default': False, 'attr': 'updated'}>
def update(self):
461    def update(self):
462        for index, command_pointer in enumerate(self.commands):
463            command = command_pointer.dereference(self)
464            command.index = index
465        updated = True
def get_active_command(self):
467    def get_active_command(self):
468        if len(self.commands) <= self.active_index:
469            return None
470        command_pointer = self.commands[self.active_index]
471        return command_pointer.dereference(self)
def new_command(self, command: str):
473    def new_command(self, command: str):
474        command_type = COMMAND_TYPE_LIST.get(command)
475        collection_name = 'misc_commands'
476        if command_type:
477            collection_name = self.command_type_collections.get(command_type.bl_idname) or collection_name
478        
479        collection = getattr(self, collection_name)
480        new_command = collection.add()
481        new_command.command = command
482        
483        new_pointer = self.commands.add()
484        new_pointer.collection_name = collection_name
485        new_pointer.prop_index = len(collection) - 1
486
487        new_command.index = len(self.commands) - 1
488
489        return new_command
def remove_command(self, index: int):
491    def remove_command(self, index: int):
492        command_pointer = self.commands[index]
493        command = command_pointer.dereference(self)
494        
495        collection_name = self.command_type_collections.get(command.bl_idname) or 'misc_commands'
496        collection = getattr(self, collection_name)
497
498        prop_index = command_pointer.prop_index
499        self.commands.remove(index)
500        self.update()
501        collection.remove(prop_index)
502
503        for i, c in enumerate(collection):
504            self.commands[c.index].prop_index = i
505
506        if self.active_index >= len(self.commands):
507            self.active_index = len(self.commands) - 1
def move_command(self, old_index, new_index, update=True):
509    def move_command(self, old_index, new_index, update=True):
510        self.commands.move(old_index, new_index)
511        self.updated = False
512        if update:
513            self.update()
def parse_list(self, string_list):
515    def parse_list(self, string_list):
516        command = string_list[0]
517        new_command = self.new_command(command)
518        new_command.parse_list(string_list)
def unpack_from_file(self, file):
520    def unpack_from_file(self, file):
521        if common.read_str(file) != 'CM3D2_MENU':
522            raise IOError("Not a valid CM3D2 .menu file.")
523
524        self.version      = struct.unpack('<i', file.read(4))[0]
525        self.path         = common.read_str(file)
526        self.name         = common.read_str(file)
527        self.category     = common.read_str(file)
528        self.description  = common.read_str(file)
529        
530        struct.unpack('<i', file.read(4))[0]
531        string_list = []
532        string_list_length = struct.unpack('<B', file.read(1))[0]
533        while string_list_length > 0:
534            string_list.clear()
535
536            for i in range(string_list_length):
537                string_list.append(common.read_str(file))
538            
539            try:
540                self.parse_list(string_list)
541            except ValueError as e:
542                print(e)
543            
544            # Check for end of file
545            chunk = file.read(1)
546            if len(chunk) == 0:
547                break
548            string_list_length = struct.unpack('<B', chunk)[0]
549
550        self.update()
def pack_into_file(self, file):
552    def pack_into_file(self, file):
553        self.update()
554
555        common.write_str(file, 'CM3D2_MENU')
556
557        file.write(struct.pack('<i', self.version    ))
558        common.write_str(file,       self.path       )
559        common.write_str(file,       self.name       )
560        common.write_str(file,       self.category   )
561        common.write_str(file,       self.description)
562                    
563        buffer = bytearray()
564        for command_pointer in self.commands:
565            buffer = command_pointer.dereference(self).pack_into(buffer)
566        buffer = buffer + struct.pack('<B', 0x00)
567        
568        file.write(struct.pack('<i', len(buffer)))
569        file.write(bytes(buffer))
def clear(self):
571    def clear(self):
572        self.property_unset('version'    )
573        self.property_unset('path'       )
574        self.property_unset('name'       )
575        self.property_unset('category'   )
576        self.property_unset('description')
577        
578        for prop in self.command_type_collections.values():
579            self.property_unset(prop)
580
581        self.property_unset('misc_commands')
582
583        self.property_unset('commands'    )
584        self.property_unset('active_index')
585
586        self.property_unset('updated')
command_type_collections = {'CM3D2MenuAttachPointCommand': 'attach_point_commands', 'CM3D2MenuMiscCommand': 'misc_commands', 'CM3D2PropertyMenuCommand': 'property_commands'}
bl_rna = <bpy_struct, Struct("OBJECT_PG_CM3D2Menu")>
Inherited Members
builtins.bpy_struct
keys
values
items
get
pop
as_pointer
keyframe_insert
keyframe_delete
driver_add
driver_remove
is_property_set
property_unset
is_property_hidden
is_property_readonly
is_property_overridable_library
property_overridable_library_set
path_resolve
path_from_id
type_recast
bl_rna_get_subclass_py
bl_rna_get_subclass
id_properties_ensure
id_properties_clear
id_properties_ui
id_data
def import_menu_func(self, context):
593def import_menu_func(self, context):
594    self.layout.operator(menu_OBJECT_PT_cm3d2_menu.CM3D2MENU_OT_import.bl_idname, text="CM3D2 Menu (.menu)", icon_value=common.kiss_icon())
def export_menu_func(self, context):
597def export_menu_func(self, context):
598    self.layout.operator(menu_OBJECT_PT_cm3d2_menu.CM3D2MENU_OT_export.bl_idname, text="CM3D2 Menu (.menu)", icon_value=common.kiss_icon())